#include "global.h"
#include "asgka_space.h"
#include "messageCommon.h"
#include "messageCommonEx.h"
#include "messageMemberList.h"
#include "asgkaCreateAgreementThread.h"
#include "asgkaAddAgreementThread.h"
#include "asgkaDelAgreementThread.h"
#include "common.h"
#include "messagetype.h"
#include "asgka_network.h"
#include "TCPListenThread.h"
#include "TCPRecvThread.h"
#include "UDPRecvThread.h"
#include <vector>

#include <iostream>
using namespace std;

memberCenter *pMemberCenter = NULL;
groupCenter *pGroupCenter = NULL;
singleMember *pLocalMember = NULL;
TCPListenThread *pTcpListener = NULL;
UDPRecvThread *pUdpRecver = NULL;

void asgka_setup(const std::string &mylocalname, HWND wnd = HWND_BROADCAST)
{
	int retval;
	WSAData was;
	retval = WSAStartup(MAKEWORD(2, 2), &was);
	if (retval != 0)
	{
		cout << "Start up error ! " << endl;
		return ;
	}

	// Initialize the global variables
	gWnd = wnd;
	localname = mylocalname;
	InitializeCriticalSection(&globalcs);
	InitializeCriticalSection(&filesync);
	logstream.open(LogFileName.c_str());

	char generator[LENGTH];
	strcpy_s(generator, LENGTH,
		"[6322069030959797239974302728815258316709002424839598348999808636855120175466090008089069870442189980465884571393312767672877644162824787053897296527046511, 2160399843694700352574210733609801044755127137866597903735353473997504189822218028062346910851264365516731536283788623762281392086125022828651245680060444]");
	setupSystem(generator, 10);

	pMemberCenter = new memberCenter();
	pGroupCenter  = new groupCenter();

	// Create myself
	pLocalMember = pMemberCenter->createMember(localname, INADDR_ANY);

	pTcpListener = new TCPListenThread(pMemberCenter, pGroupCenter);
	pUdpRecver = new UDPRecvThread(pMemberCenter, pGroupCenter);
	pTcpListener->start();
	pUdpRecver->start();
}

void asgka_msgToWnd(HWND wnd = HWND_BROADCAST)
{
	gWnd = wnd;
}

void asgka_close()
{
	if (pMemberCenter != NULL)
	{
		messageBase *olmsg = new messageCommon(localname, NET_MESSAGE_OFF_LINE);
		pMemberCenter->sendMessageToAllMember(olmsg);
		delete olmsg;
		Sleep(ExitWait);
	}
	if (TCPListenSock != INVALID_SOCKET)
		closesocket(TCPListenSock);
	if (UDPRecvSock != INVALID_SOCKET)
		closesocket(UDPRecvSock);
	if (pTcpListener != NULL)
		delete pTcpListener;
	if (pUdpRecver != NULL)
		delete pUdpRecver;
	if (pGroupCenter != NULL)
		delete pGroupCenter;
	if (pMemberCenter != NULL)
		delete pMemberCenter;
	TCPListenSock = INVALID_SOCKET;
	UDPRecvSock = INVALID_SOCKET;
	pTcpListener = NULL;
	pUdpRecver = NULL;
	pGroupCenter = NULL;
	pMemberCenter = NULL;
	closeSystem();
	DeleteCriticalSection(&globalcs);
	DeleteCriticalSection(&filesync);
	logstream.flush();
	logstream.close();
	WSACleanup();
}

void asgka_search()
{
	// Search member
	messageBase *searchmsg = new messageCommon(localname, NET_MESSAGE_SEARCH);
	broadcastMessage(searchmsg);
	delete searchmsg;
}

void asgka_leaveGroup(const std::string &groupname)
{
	messageBase *lgmsg = new messageCommonEx(localname, groupname, NET_MESSAGE_LEAVE_GROUP);
	pGroupCenter->sendMessageToGroup(lgmsg, groupname);
	pGroupCenter->releaseGroup(groupname);
	delete lgmsg;
}

std::map<std::string, u_long> asgka_getGroupMember(const std::string &groupname)
{
	singleGroup *pGroup = NULL;
	pGroup = pGroupCenter->getGroupByName(groupname);
	std::map<std::string, u_long> member_map;

	if (pGroup == NULL)
	{
		return member_map;
	}

	pGroup->generateMemberList(member_map);
	return member_map;
}

void asgka_createGroup(const std::string &groupname, const std::vector<std::string> &member_list)
{
	singleGroup *pGroup = NULL;
	pGroup = pGroupCenter->createGroup(groupname, pLocalMember);
	std::map<std::string, u_long> member_map;

	if (pGroup == NULL)
	{
		return ;
	}
	// Send invitation
	messageBase *imsg = new messageCommonEx(localname, pGroup->getGroupName(), NET_MESSAGE_GROUP_INVITATION);
	singleMember *pMember = NULL;
	std::vector<std::string>::const_iterator cit = member_list.begin();
	for (; cit != member_list.end(); cit++)
	{
		pMember = pMemberCenter->getMemberByName(*cit);
		if (pMember != NULL)
		{
			sendMessageToMember_tcp(imsg, pMember);
			pGroup->addAMember(pMember);
			member_map.insert(stouvalType(*cit, pMember->getNetAddr()));
		}
	}
	delete imsg;

	Sleep(MemberListWait);

	// Send member list
	messageBase *mlmsg = new messageMemberList(pGroup->getGroupName(), member_map);
	pGroupCenter->sendMessageToGroup(mlmsg, groupname);

	asgkaCreateAgreementThread *pthread = new asgkaCreateAgreementThread(pGroup);
	pthread->start();

	delete mlmsg;
	return ;
}

std::map<std::string, u_long> asgka_getMemberList()
{
	std::map<std::string, u_long> members;
	const std::map<std::string, singleMember *> *retval = pMemberCenter->getAddrofMemberList();
	std::map<std::string, singleMember *>::const_iterator it = retval->begin();
	for (; it != retval->end(); it++)
	{
		members.insert(stouvalType(it->first, it->second->getNetAddr()));
	}
	return members;
}

std::string asgka_getGroupLeaderName(const std::string &groupname)
{
	singleGroup *pGroup = pGroupCenter->getGroupByName(groupname);
	return (pGroup->getGroupLeader()->getMemberName());
}

int asgka_joinGroup(const std::string &groupname, const std::string &groupleadername)
{
	singleMember *pMember = pMemberCenter->getMemberByName(groupleadername);
	if (pMember == NULL)
		return 1;

	messageBase *msg = new messageCommonEx(localname, groupname,  NET_MESSAGE_JOIN_GROUP);
	sendMessageToMember_tcp(msg, pMember);
	delete msg;
	return 0;
}

void asgka_kickOut(const std::string &groupname, const std::string &membername)
{
	singleGroup *pGroup = pGroupCenter->getGroupByName(groupname);
	if (localname.compare(pGroup->getGroupLeader()->getMemberName()) != 0)
		return ;
	messageBase *komsg = new messageCommonEx(membername, groupname, NET_MESSAGE_KICK_OUT);
	pGroupCenter->sendMessageToGroup(komsg, groupname);

	Sleep(CommonWait);

	pGroupCenter->DelMemberFromGroup(groupname, membername);
}

void asgka_addMember(const std::string &groupname, const std::string &membername)
{
	asgka_search();
	singleGroup *pGroup = pGroupCenter->getGroupByName(groupname);
	singleMember *pMember = pMemberCenter->getMemberByName(membername);

	if ((pGroup == NULL) || (pMember == NULL))
	{
		cout << "Error name" << endl;
		return;
	}

	// Check limitation
	if (localname.compare(pGroup->getGroupLeader()->getMemberName()) != 0)
	{
		cout << "You can not add a member to the group!" << endl;
		return;
	}
	
	if (true) // Some conditions
	{
		// Send group invitation
		messageBase *pmsg = new messageCommonEx(localname, groupname, NET_MESSAGE_GROUP_INVITATION);
		sendMessageToMember_tcp(pmsg, pMember);

		// Add sender to member
		pGroup->addAMember(pMember);

		// Generate member list
		std::map<std::string, u_long> member_list;
		pGroup->generateMemberList(member_list);

		// Send member list to new member
		messageBase *pmlmsg = new messageMemberList(pGroup->getGroupName(), member_list);
		sendMessageToMember_tcp(pmlmsg, pMember);

		// Wait for the new member
		Sleep(NewMemberWait);

		// Send new member to old group members
		messageBase *pnmsg = new messageCommonEx(pMember->getMemberName(), 
			pGroup->getGroupName(), NET_MESSAGE_MEMBER_ADD);
		pGroupCenter->sendMessageToGroup(pnmsg, pGroup->getGroupName());

		delete pmsg;
		delete pmlmsg;
		delete pnmsg;

		asgkaAddAgreementThread *pThread = new asgkaAddAgreementThread(pGroup, membername);
		pThread->start();

		cout << membername << " join the " << groupname << endl;
	}
}
